import java.io.Serializable;
import java.util.*;

class PanelSize {
    public static int
            XLength = 80,
            YLength = 50,
            cell = 14;
}

//方向枚举类
enum Directions implements Serializable {
    UP, DOWN, LEFT, RIGHT
}

//节点类，抽象的食物，也是蛇的组成部分
class Node implements Serializable {
    private int x, y;


    public Node() {

    }

    public Node(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public boolean same(Node n) {
        return (x == n.getX()) && (y == n.getY());
    }

    public int getX() {
        return this.x;
    }

    public int getY() {
        return this.y;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    //随机生成节点，round代表范围
    public void random(int roundX, int roundY) {
        Random r = new Random();
        this.x = r.nextInt(roundX);
        this.y = r.nextInt(roundY);
    }
}

class Food extends Node implements Serializable {
    public Food() {
        super();
    }

    public Food(int roundX, int roundY) {
        super();
        this.roundX = roundX;
        this.roundY = roundY;

    }

    public void generate(Collection snakeSet, LinkedList<Node> wall) {

        while (!isGoodFood(snakeSet, wall))
            random(roundX, roundY);
    }

    public boolean isGoodFood(Collection snakeSet, LinkedList<Node> wall) {
        if (snakeSet != null) {
            //此处要用迭代器iterator
            Iterator<Snake> iSnake = snakeSet.iterator();
            while (iSnake.hasNext()){
                Snake snake = iSnake.next();
                for (Node n : snake.getBody()) {
                    if (same(n))
                        return false;
                }
            }
        }
        for (Node n : wall) {
            if (same(n))
                return false;
        }
        return true;
    }

    private int roundX, roundY;

}

//地图类  含有食物和障碍物
class GameMap implements Serializable {
    private LinkedList<Node> wall;
    private Food food;

    public GameMap() {
        initWall();
        initFood();
    }

    public void initWall() {
        wall = new LinkedList<>();
//        北墙 x 是 0到xlength ,y 是0
        for (int x = 0; x < PanelSize.XLength - 1; x++) {
            Node n = new Node(x, 0);
            wall.add(n);
        }
//        南墙 x 是 0到xlength-1 ,y 是 Ylength - 1
        for (int x = 0; x < PanelSize.XLength; x++) {
            Node n = new Node(x, PanelSize.YLength - 1);
            wall.add(n);
        }
//        西墙 x 是 0 ,y 是0到ylength-1
        for (int y = 0; y < PanelSize.YLength; y++) {
            Node n = new Node(0, y);
            wall.add(n);
        }
//        东墙 x 是 xlength-1 ,y 是0到ylength-1
        for (int y = 0; y < PanelSize.YLength; y++) {
            Node n = new Node(PanelSize.XLength - 1, y);
            wall.add(n);
        }

    }

    public void initFood() {
        food = new Food(PanelSize.XLength - 1, PanelSize.YLength - 1);
        food.generate(null, wall);
    }

    public Food getFood() {
        return food;
    }

    public LinkedList<Node> getWall() {
        return wall;
    }
}


//蛇
class Snake implements Serializable {
    private LinkedList<Node> body;
    private int bodyLength;
    private boolean isLiving;
    private boolean isEating;
    private int startPointX = 10, startPointY = 10;
    private Directions d = Directions.RIGHT;
    private Node head;
    private int initLength = 3;

    public Snake() {

        isEating = false;
        isLiving = true;
        body = new LinkedList<>();
        head = new Node();
        this.bodyLength = 0;
        body.addFirst(new Node(startPointX, startPointY));
        for (int i = 1; i < initLength; i++) {
            grow();
        }
    }

    public void grow() {
        Node newNode = new Node();
        Node head = getHead();
        switch (d) {
            case UP:
                newNode.setX(head.getX());
                newNode.setY(head.getY() - 1);
                break;
            case DOWN:
                newNode.setX(head.getX());
                newNode.setY(head.getY() + 1);
                break;
            case LEFT:
                newNode.setX(head.getX() - 1);
                newNode.setY(head.getY());
                break;
            case RIGHT:
                newNode.setX(head.getX() + 1);
                newNode.setY(head.getY());
                break;
        }
        body.addFirst(newNode);
        bodyLength++;
        getHead();
        body = getBody();

    }

    //前进
    public void advance() {
        //先往前生长一格
        grow();
        //如果不在吃东西，就把尾部往前挪一格，否则尾部位置仍然保留
        if (!isEating) {
            body.removeLast();
            bodyLength--;
        }
        isEating = false;//前进后，设置为不在吃东西

    }

    //撞墙
    public boolean hitWall(LinkedList<Node> wall) {
        for (Node n : wall) {
            if (head.same(n)) {
                die();
                return true;
            }
        }
        return false;
    }

    //咬到自己
    public boolean biteSelf() {
        for (Node n : body) {
            if (n.equals(head))
                continue;
            if (head.same(n)) {
                die();
                return true;
            }
        }
        return false;
    }

    //死亡
    public void die() {
        this.bodyLength = 0;
        isLiving = false;

    }


    public boolean eat(Food food) {
        if (head.same(food)) {
            isEating = true;
            return true;
        }
        return false;
    }

    public void move(GameMap gameMap) {
        if (!isLiving)
            return;
        advance();
        //判断蛇是否吃食物
        /*if (eat(gameMap.getFood())) {
            gameMap.getFood().generate(body, gameMap.getWall());
        }*/
        eat(gameMap.getFood());
        //判断蛇是否撞墙
        hitWall(gameMap.getWall());
        //判断蛇是否咬到自己
        biteSelf();

    }

    public boolean isLiving() {
        return isLiving;
    }

    public boolean isEating() {
        return isEating;
    }

    public LinkedList<Node> getBody() {
        return body;
    }

    public int getBodyLength() {
        return bodyLength;
    }

    public Node getHead() {
        head = body.getFirst();
        return head;
    }

    public void setDirection(Directions d) {
        this.d = d;
    }

    public Directions getD() {
        return d;
    }

    //复活
    public void revive() {
        isLiving = true;
        body = new LinkedList<>();
        head = new Node();
        d = Directions.RIGHT;
        body.addFirst(new Node(startPointX, startPointY));
        for (int i = 1; i < initLength; i++) {
            grow();
        }
    }
}

